using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace ReportMaintenanceUtility
{
	public abstract class CustomList<T> : BindingList<T>, IStateManagement<CustomList<T>> where T : GenericBase<T>
	{
		#region PRIVATE MEMBERS
		protected bool _isDirty = false;
		protected CustomList<T> _copy = null;
		protected bool _initializing = false;
		protected object _seed = null;
		#endregion

		#region PUBLIC PROPERTIES
		public bool IsDirty
		{
			get
			{
				return this._isDirty;
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		public CustomList() : this(new List<T>()) { }
		public CustomList(List<T> list) : base(list)
		{
			this.AllowEdit = true;
			this.AllowNew = true;
			this.AllowRemove = true;
			this.RaiseListChangedEvents = true;
		}
		protected CustomList(CustomList<T> list) : this()
		{
			this._initializing = true;

			foreach (T obj in list)
			{
				this.Add(obj.CreateDeepCopy());
			}

			this._initializing = false;
		}
		#endregion

		#region INTERFACE IMPLEMENTATIONS
		public bool EqualTo(CustomList<T> other)
		{
			if (other == null)
			{
				return false;
			}
			else if (this.Count != other.Count)
			{
				return false;
			}
			else
			{
				for (int i = 0; i < this.Count; i++)
				{
					if (!this[i].EqualTo(other[i]))
					{
						return false;
					}
				}
				return true;
			}
		}
		public List<ChangeLog> SaveState()
		{
			List<ChangeLog> log = new List<ChangeLog>();
			foreach (T obj in this.Items)
			{
				if (obj.IsDirty)
				{
					log.AddRange(obj.SaveState());
				}
			}
			this.ClearState();
			return log;
		}

		private void ClearState()
		{
			this._copy = null;
			this._isDirty = false;
		}
		public void RestoreState()
		{
			if (this._copy != null)
			{
				this._initializing = true;
				this._seed = this._copy._seed;
				this.Items.Clear();
				foreach(T obj in this._copy.Items)
				{
					this.Items.Add(obj);
				}
				this._initializing = false;
			}
			this.ClearState();
		}
		public void PrepStateChange(string propertyName)
		{
			if (!this._isDirty)
			{
				this._copy = this.CreateDeepCopy();
				this._isDirty = true;
			}
			this.NotifyPropertyChanged(propertyName);
		}

		public event PropertyChangedEventHandler PropertyChanged;
		protected void NotifyPropertyChanged(String info)
		{
			if (this.PropertyChanged != null)
			{
				this.PropertyChanged(this, new PropertyChangedEventArgs(info));
			}
		}

		public abstract CustomList<T> CreateDeepCopy();
		public object GetSeedObject()
		{
			return this._seed;
		}
		#endregion
	}
}

#region OLD CODE
//public class CustomList<T> : ObservableCollection<T>, IEquatable<CustomList<T>>, IStateManagement<CustomList<T>> where T : GenericBase<T>
//{
//    #region PRIVATE MEMBERS
//    private bool _isDirty = false;
//    private CustomList<T> _copy = null;
//    private bool _initializing = false;
//    #endregion

//    #region PUBLIC PROPERTIES
//    public bool IsDirty
//    {
//        get
//        {
//            return this._isDirty;
//        }
//    }
//    public event PropertyChangedEventHandler PropertyChanged;
//    #endregion

//    #region CONSTRUCTOR(S)
//    private CustomList(CustomList<T> list)
//    {
//        this._initializing = true;
//        foreach (T obj in list)
//        {
//            this.Add(obj.CreateDeepCopy());
//        }
//        this._initializing = false;
//    }
//    public CustomList() : this(new List<T>()) { }
//    public CustomList(List<T> list) : base(list)
//    {
//        this.CollectionChanged += new NotifyCollectionChangedEventHandler(CustomList_CollectionChanged);
//    }
//    #endregion

//    #region PRIVATE / PROTECTED METHODS
//    //triggered when the collection changes... (need to clarify if this only applies to Inserts / Deletes / Sets, or if property changes also bubble-up to this level..
//    private void CustomList_CollectionChanged(object o, NotifyCollectionChangedEventArgs args)
//    {
//        this._isDirty = true;
//    }

//    //receives PropertyChanged event messages from objects within collection...
//    protected void PropertyChangedListener(object sender, PropertyChangedEventArgs args)
//    {
//        NotifyPropertyChanged(string.Format("{0}.{1}", this.GetType().Name, args.PropertyName));
//    }

//    //re-broadcasts the PropertyChanged events from objects within collection...
//    protected void NotifyPropertyChanged(String info)
//    {
//        if (this.PropertyChanged != null)
//        {
//            this.PropertyChanged(this, new PropertyChangedEventArgs(string.Format("{0}.{1}", this.GetType().Name, info)));
//        }
//    }
//    #endregion

//    #region PUBLIC METHODS
//    public void AddRange(IEnumerable<T> collection)
//    {
//        ((List<T>)this.Items).AddRange(collection);
//    }
//    public T Find(Predicate<T> match)
//    {
//        return ((List<T>)this.Items).Find(match);
//    }
//    public IEnumerable<T> FindAll(Predicate<T> match)
//    {
//        return ((List<T>)this.Items).FindAll(match);
//    }
//    public T[] ToArray()
//    {
//        return ((List<T>)this.Items).ToArray();
//    }
//    #endregion

//    #region OVERRIDES
//    protected override void InsertItem(int index, T item)
//    {
//        if (!this._initializing)
//        {
//            this.PrepStateChange();
//            this._isDirty = true;
//            item.PropertyChanged += this.PropertyChangedListener;
//        }
//        base.InsertItem(index, item);
//    }
//    protected override void RemoveItem(int index)
//    {
//        if (!this._initializing)
//        {
//            this.PrepStateChange();
//            this._isDirty = true;
//        }
//        base.RemoveItem(index);
//    }
//    protected override void SetItem(int index, T item)
//    {
//        if (!this._initializing)
//        {
//            this.PrepStateChange();
//            this._isDirty = true;
//            item.PropertyChanged += this.PropertyChangedListener;
//        }
//        base.SetItem(index, item);
//    }
//    protected override void ClearItems()
//    {
//        if (!this._initializing)
//        {
//            this.PrepStateChange();
//            this._isDirty = true;
//        }
//        base.ClearItems();
//    }
//    #endregion

//    #region INTERFACE IMPLEMENTATIONS
//    public bool Equals(CustomList<T> other)
//    {
//        if (other == null)
//        {
//            return false;
//        }
//        else if (this.Count != other.Count)
//        {
//            return false;
//        }
//        else
//        {
//            for (int i = 0; i < this.Count; i++)
//            {
//                if (!this[i].Equals(other[i]))
//                {
//                    return false;
//                }
//            }
//            return true;
//        }
//    }
//    public List<ChangeLog> SaveState()
//    {
//        List<ChangeLog> log = new List<ChangeLog>();
//        foreach (T obj in this.Items)
//        {
//            if (obj.IsDirty)
//            {
//                log.AddRange(obj.SaveState());
//            }
//        }

//        this._isDirty = false;
//        this._copy = null;

//        return log;
//    }
//    public void RestoreState()
//    {
//        if (this._copy != null)
//        {
//            throw new Exception("The method or operation is not implemented.");
//        }
//    }
//    public void PrepStateChange()
//    {
//        if (!this._isDirty)
//        {
//            this._copy = this.CreateDeepCopy();
//        }
//    }
//    public CustomList<T> CreateDeepCopy()
//    {
//        return new CustomList<T>(this);
//    }
//    #endregion
//}
#endregion